Package bg.smoc.agent

Source Code of bg.smoc.agent.GraderAgent

package bg.smoc.agent;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;

import kr.or.ioi2002.RMIServer.Job;
import kr.or.ioi2002.RMIServer.Syslog;
import kr.or.ioi2002.RMIServer.TempFile;
import kr.or.ioi2002.RMIServer.Job.JobType;
import kr.or.ioi2002.RMIServer.agent.AgentException;
import kr.or.ioi2002.RMIServer.agent.LogAgentIO;
import bg.smoc.model.manager.ContestManager;
import bg.smoc.model.manager.GraderManager;

// public: accessed from Contest to generate post data
public class GraderAgent extends Thread {
    private static final int GRADE_PROCESS_TIMEOUT = 600 * 1000 * 2; // 1200 sec

    private static int SUBMIT_PROCESS_TIMEOUT = 100000; // 100 sec

    private static int TEST_PROCESS_TIMEOUT = 600000; // 600 sec

    private GraderManager graderManager;

    @SuppressWarnings("unused")
    private ContestManager contestManager;

    public GraderAgent(GraderManager graderManager, ContestManager contestManager, Socket socket) {
        this.socket = socket;
        setDaemon(true);
        this.graderManager = graderManager;
        this.contestManager = contestManager;
        start();
    }

    public void postMessageEntry() {
        currentJob = null;
        graderManager.OnMsgGaEntry(this);
    }

    public void postMessageFail() {
        graderManager.OnMsgGaFail(this, resetCurrentJob());
    }

    /**
     * Returns the Job which until this call was current. And resets the current
     * job to null.
     *
     * @return the just finished job
     */
    private Job resetCurrentJob() {
        Job finishedJob = currentJob;
        currentJob = null;
        return finishedJob;
    }

    protected void doJob() throws IOException, AgentException {
        if ("SETUP".equals(currentJob.getType())) {
            // Contest contest =
            // contestManager.getContest(currentJob.getContestId());
            // for (Task task : contest.getTasks()) {
            // writeLine("DATA " + task.getName());
            // sendFile(contestManager.updateTestGroupsFile(contest.getId(),
            // task));
            // TODO verification code should go here.
            // }
            currentJob = null;
        } else {
            if (!(JobType.FEEDBACK.equals(currentJob.getType())
                    || JobType.GRADE.equals(currentJob.getType())
                    || JobType.SUBMIT.equals(currentJob.getType()) || JobType.TEST
                    .equals(currentJob.getType()))) {
                Syslog.log("!Discarding Job, GraderAgent:"
                        + " doJob: Job invaild, type:"
                        + (currentJob.getType() != null ? currentJob.getType() : "null"));
                currentJob = null;
                return;
            }

            sendSourceCode();
            readResponse();
            handleRurnedData();
            sendDoneMessage();
        }
    }

    private int getTimeoutForJobType(JobType jobType) throws AgentException {
        if (JobType.GRADE.equals(currentJob.getType()))
            return GRADE_PROCESS_TIMEOUT;
        if (JobType.SUBMIT.equals(currentJob.getType()))
            return SUBMIT_PROCESS_TIMEOUT;
        if (JobType.FEEDBACK.equals(currentJob.getType()))
            return SUBMIT_PROCESS_TIMEOUT;
        if (JobType.TEST.equals(currentJob.getType()))
            return TEST_PROCESS_TIMEOUT;

        throw new AgentException("Invalid job type - not supported by this agent.");
    }

    private void sendSourceCode() throws IOException {
        writeLine("REQUEST "
                + ((currentJob.getType() != JobType.FEEDBACK) ? currentJob.getType().toString()
                        : "GRADE")
                + " "
                + currentJob.getTask()
                + " "
                + currentJob.getLanguage());
        sendFile(currentJob.src);
        if (JobType.TEST.equals(currentJob.getType())) {
            if (currentJob.stdin != null)
                sendFile(currentJob.stdin);
            else
                sendFileNull();
        }
        os.flush();
    }

    private void readResponse() throws AgentException, IOException {
        socket.setSoTimeout(getTimeoutForJobType(currentJob.getType()));
        String command = readLine();
        if (command == null)
            throw new AgentException("RESULT expected, but connection terminated; " + command);
        StringTokenizer tokenizer = new StringTokenizer(command, " ");
        try {
            if (!tokenizer.nextToken().equals("RESULT"))
                throw new AgentException("RESULT expected; " + command);
            assertHasMoreTokens(tokenizer, command);
            String expectedType = currentJob.getType().toString();
            if (JobType.FEEDBACK.equals(currentJob.getType())) {
                expectedType = "GRADE";
            }
            if (!tokenizer.nextToken().equals(expectedType))
                throw new AgentException(currentJob.getType() + " expected; " + command);
            assertHasMoreTokens(tokenizer, command);
            String result = tokenizer.nextToken();
            if (result.equals("OK") || result.equals("FAIL"))
                currentJob.result = result;
            else
                throw new AgentException("OK or FAIL expected;" + command);
            assertHasMoreTokens(tokenizer, command);
            currentJob.setTask(tokenizer.nextToken());
        } catch (NoSuchElementException e) {
            throw new AgentException("NoSuchElementException: error parsing RESULT");
        }
        currentJob.output = recvBytes();
    }

    private void assertHasMoreTokens(StringTokenizer t, String command) throws AgentException {
        if (!t.hasMoreTokens())
            throw new AgentException("invalid RESULT format from grading machine; " + command);
    }

    private void handleRurnedData() throws IOException, AgentException {
        if (!(JobType.GRADE.equals(currentJob.getType()) || JobType.FEEDBACK.equals(currentJob
                .getType())))
            return;

        // recv grader_log
        currentJob.log = recvBytes();

        if (currentJob.getUserid() == null || currentJob.getTask() == null)
            throw new AgentException(
                    "!currentJob.userid == null || currentJob.task == null: doGrade");

        // store grader-generated files to the server
        // delete previous
        clearGradeResultFile(currentJob.getContestId(), currentJob.getUserid(), currentJob
                .getTask());

        // save csv and log as file
        TempFile tmpfilelog = TempFile.createFromByteArray(currentJob.log);
        saveGradeResultFile(currentJob.getContestId(),
                currentJob.getUserid(),
                currentJob.getTask(),
                tmpfilelog,
                currentJob.getTask() + ".grader.log");
        TempFile tmpfilecsv = TempFile.createFromByteArray(currentJob.output);
        saveGradeResultFile(currentJob.getContestId(),
                currentJob.getUserid(),
                currentJob.getTask(),
                tmpfilecsv,
                currentJob.getTask() + ".grader.csv");

        // retreive filelist and recv files
        byte[] abyFilelist = recvBytes();
        String strFilelist = new String(abyFilelist);
        StringTokenizer tFile = new StringTokenizer(strFilelist, "\n");
        try {
            String strFilename = null;
            while (tFile.hasMoreTokens() && !(strFilename = tFile.nextToken().trim()).equals("")) {
                TempFile tmpFile = recvFile();
                saveGradeResultFile(currentJob.getContestId(), currentJob.getUserid(), currentJob
                        .getTask(), tmpFile, strFilename);
            }
        } catch (NoSuchElementException e) {
            throw new AgentException("!NoSuchElementException: doGrade");
        }
    }

    private void sendDoneMessage() {
        currentJob.gmid = getIP().toString();

        // It is important that currentJob is null-ified before a call to the
        // gradeManager occurs, otherwise a putback occurs and this go awry.
        if (JobType.GRADE.equals(currentJob.getType()))
            graderManager.OnMsgGradeDone(this, resetCurrentJob());
        else if (JobType.SUBMIT.equals(currentJob.getType()))
            graderManager.OnMsgSubmitDone(this, resetCurrentJob());
        else if (JobType.TEST.equals(currentJob.getType()))
            graderManager.OnMsgTestDone(this, resetCurrentJob());
        else if (JobType.FEEDBACK.equals(currentJob.getType()))
            graderManager.OnMsgFeedbackDone(this, resetCurrentJob());
    }

    private void saveGradeResultFile(String contestId, String userid, String task, TempFile tmp,
            String strFilename) {
        if (userid == null || task == null || tmp == null || strFilename == null) {
            Syslog.log("saveGradeResultFile: (userid == null || task == null "
                    + "|| tmp == null || strFilename == null)");
            return;
        }

        File taskpath = graderManager.getTaskPath(contestId, userid, task);
        if (!taskpath.isDirectory()) {
            if (!taskpath.mkdirs()) {
                Syslog.log("!saveGradeResultFile: " + "taskpath.mkdirs() returned false");
                return;
            }
        }

        File file = new File(taskpath, strFilename);
        // overwrite previous
        if (file.exists()) {
            Syslog.log("!saveGradeResultFile: "
                    + "file already exists, trying to overwrite: "
                    + file);
            if (!file.delete())
                Syslog.log("!delete failed: saveGradeResultFile: " + file);
        }

        if (!tmp.makePermanent(file)) {
            Syslog.log("!saveGradeResultFile: " + "TempFile.makePermanent returned false: " + file);
        }
    }

    private void clearGradeResultFile(String contestId, String userid, String task) {
        if (userid == null || task == null) {
            Syslog.log("clearGradeResultFile: " + "(userid == null || task == null)");
            return;
        }

        File taskpath = graderManager.getTaskPath(contestId, userid, task);
        if (taskpath.exists()) {
            if (!taskpath.isDirectory()) {
                Syslog.log("!clearGradeResultFile:" + " taskpath is not a directory: " + taskpath);
                return;
            }

            // delete files in the directory
            String[] astrFilename = taskpath.list();
            if (astrFilename == null) {
                Syslog
                        .log("!clearGradeResultFile: "
                                + "taskpath.list() returned null: "
                                + taskpath);
                return;
            }
            for (int i = 0; i < astrFilename.length; i++) {
                File file = new File(taskpath, astrFilename[i]);
                boolean bResult = file.delete();
                if (!bResult) {
                    Syslog.log("!clearGradeResultFile: delete() failed: " + file);
                }
            }

            // delete directory
            if (!taskpath.delete()) {
                Syslog.log("!clearGradeResultFile: "
                        + "deleting task directory failed: "
                        + taskpath);
                return;
            }
        }

    }

    protected static long FILESIZE_LIMIT = 100 * 1024 * 1024;
    protected static int FILE_TRANSFER_INTEGRITY_KEY = 0x37fd8a20;

    protected String agentVersion = null;
    protected Job currentJob = null;
    protected BufferedInputStream is = null;
    protected BufferedOutputStream os = null;
    protected Socket socket = null;

    protected LinkedList<String> contestsToBeUpdated = new LinkedList<String>();

    protected Integer cs = new Integer(0); // critical section for synchronized

    public String getVersion() {
        return agentVersion;
    }

    public InetAddress getIP() {
        return socket.getInetAddress();
    }

    public Job getJob() {
        return currentJob;
    }

    public boolean assignJob(Job job) {
        if (this.currentJob != null)
            return false;
        currentJob = job;
        synchronized (cs) {
            cs.notifyAll();
        }
        return true;
    }

    protected void waitforJob() throws InterruptedException, IOException {
        while (currentJob == null) {
            synchronized (cs) {
                if (currentJob == null)
                    cs.wait(10000); // Keep Alive 10000 sec
            }
            os.write('\n');
            os.flush();
        }
    }

    public void run() {
        try {
            is = new BufferedInputStream(socket.getInputStream());
            os = new BufferedOutputStream(socket.getOutputStream());

            String command = null;

            // expect READY in 10 sec
            socket.setSoTimeout(10000);
            command = readLine();
            if (command.length() < 5)
                throw new AgentException("READY expected; " + command);
            if (!command.substring(0, 5).equals("READY"))
                throw new AgentException("READY expected; " + command);
            if (command.length() > 5) // version information available
            {
                agentVersion = command.substring(5).trim();
            }
            writeLine("ACK");
            os.flush();

            postMessageEntry();

            while (true) {
                if (currentJob == null)
                    waitforJob();
                doJob();
            }

        } catch (AgentException e) {
            Syslog.log(e.toString());
        } catch (IOException e) {
            LogAgentIO.log(e.toString());
        } catch (InterruptedException e) {
            Syslog.log(e.toString());
        }

        try {
            is.close();
            os.close();
            socket.close();
        } catch (IOException e) {
            Syslog.log(e.toString());
        }

        postMessageFail();

        System.out.println("IOI Server: Agent Disconnected [" + getIP() + "]");
    }

    protected void sendFile(File file) throws IOException {
        long length = file.length();
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));

        ByteArrayOutputStream baos = new ByteArrayOutputStream(8);
        DataOutputStream dos = new DataOutputStream(baos);
        dos.writeInt(FILE_TRANSFER_INTEGRITY_KEY); // keyvalue
        dos.writeInt((int) length);
        byte[] bHeader = baos.toByteArray();
        dos.close();
        baos.close();

        os.write(bHeader);
        byte[] bBuffer = new byte[1024];
        int iTotal = 0;
        int iRead;
        while ((iRead = bis.read(bBuffer)) > 0) {
            os.write(bBuffer, 0, iRead);
            iTotal += iRead;
        }
        bis.close();
    }

    protected void sendFileNull() throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(8);
        DataOutputStream dos = new DataOutputStream(baos);
        dos.writeInt(FILE_TRANSFER_INTEGRITY_KEY); // keyvalue
        dos.writeInt(0);
        byte[] bHeader = baos.toByteArray();
        dos.close();
        baos.close();

        os.write(bHeader);
    }

    protected TempFile recvFile() throws IOException, AgentException {
        byte[] bHeader = new byte[8];
        int toRead = 8;
        while (toRead > 0) {
            int iRead = is.read(bHeader, 8 - toRead, toRead);
            if (iRead < 0)
                throw new AgentException("closed during recvFile");
            toRead -= iRead;
        }
        DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bHeader));
        int keyvalue = dis.readInt();
        if (keyvalue != FILE_TRANSFER_INTEGRITY_KEY)
            throw new AgentException("recvFile: Wrong Integrity Key");
        int length = dis.readInt();
        dis.close();

        if (length > FILESIZE_LIMIT)
            throw new AgentException("recvFile: FILESIZE_LIMIT("
                    + String.valueOf(FILESIZE_LIMIT)
                    + ") exceeded, length="
                    + String.valueOf(length));

        return TempFile.createFromStream(is, length);
    }

    protected byte[] recvBytes() throws IOException, AgentException {
        byte[] bHeader = new byte[8];
        int toRead = 8;
        while (toRead > 0) {
            int iRead = is.read(bHeader, 8 - toRead, toRead);
            if (iRead < 0)
                throw new AgentException("closed during recvBytes");
            toRead -= iRead;
        }
        DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bHeader));
        int keyvalue = dis.readInt();
        if (keyvalue != FILE_TRANSFER_INTEGRITY_KEY)
            throw new AgentException("recvBytes: Wrong Integrity Key");
        int length = dis.readInt();
        dis.close();

        if (length < 0 || length > FILESIZE_LIMIT)
            throw new AgentException("recvBytes: FILESIZE_LIMIT("
                    + String.valueOf(FILESIZE_LIMIT)
                    + ") exceeded, length="
                    + String.valueOf(length));

        byte[] buffer = new byte[length];
        int iRead = 0;
        int iOffset = 0;
        while (iOffset < length && (iRead = is.read(buffer, iOffset, length - iOffset)) > 0) {
            iOffset += iRead;
        }

        return buffer;
    }

    protected String readLine() throws IOException, AgentException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(80);
        int iRead = -1;
        do {
            iRead = is.read();
            if (iRead < 0)
                throw new AgentException("closed during readLine");
            baos.write(iRead);
        } while (iRead != '\n');
        return baos.toString().trim();
    }

    protected void writeLine(String line) throws IOException {
        byte[] bLine = line.getBytes();
        os.write(bLine);
        os.write('\n');
    }

    public void setNeedsUpdate(String id) {
        contestsToBeUpdated.add(id);
    }
}
TOP

Related Classes of bg.smoc.agent.GraderAgent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.